//
//  Publishers+Materialize.swift
//  GerritHermes
//
//  Created by J.Zhou on 2020/4/7.
//  Copyright © 2020 周剑. All rights reserved.
//

import Combine

extension Publishers {
    public enum Event<T, E: Error> {
        case value(T)
        case failure(Error)
        case finished
    }

    public class Materialized<Upstream: Publisher>: Publisher {
        public typealias Output = Event<Upstream.Output, Upstream.Failure>
        public typealias Failure = Never

        private let upstream: Upstream

        init(upstream: Upstream) {
            self.upstream = upstream
        }

        public func receive<S>(subscriber: S) where S : Subscriber, Materialized.Failure == S.Failure, Materialized.Output == S.Input {
            let cancellable = upstream.sink(receiveCompletion: { completion in
                switch completion {
                case .failure(let error):
                    _ = subscriber.receive(.failure(error))
                case .finished:
                    _ = subscriber.receive(.finished)
                }
                subscriber.receive(completion: .finished)
            }, receiveValue: { output in
                _ = subscriber.receive(.value(output))
            })
            let subscription = MaterializedSubscription(cancellable: cancellable)
            subscriber.receive(subscription: subscription)
        }

        private class MaterializedSubscription: Subscription {
            private let cancellable: AnyCancellable

            init(cancellable: AnyCancellable) {
                self.cancellable = cancellable
            }

            func request(_ demand: Subscribers.Demand) {}

            func cancel() {
                cancellable.cancel()
            }
        }
    }
}

extension Publisher {
    func materialize() -> Publishers.Materialized<Self> {
        Publishers.Materialized(upstream: self)
    }
}
